﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Data.Common;
using System.Data;
using System.Diagnostics;
using Dataglot.CrossDb;
using Dataglot.Utils;

namespace Dataglot
{

    public enum DbBrand
    {
        None = 0,

        //Propriety and 'different'
        MsSql2000 = 1, //Should work with 7, MSDB, Sql2000
        MsSql2005 = 2, //For 2005,2008,
        SQLite = 3,
        MySql = 4,
        MsAccess = 5,
        SqlCompact35 = 6, //Not ASP.NET compliant
        SqlCompact4 = 7, //Not supposed to use 3 or earlier in ASP.NET
        OracleOracle = 8,
        MsOracle = 9, //Deprecated

        //Standards based protocols
        Odbc = 10, //Some SQL may fail.
        OleDb = 11

    }


    /// <summary>
    /// 
    /// This class wraps DbProviderFactory because it sealed.
    /// 
    /// Provide a place to hold state when what could have been an extension method 
    /// requires state.
    /// 
    /// </summary>
    public class DataFactory
    {

        /// <summary>
        /// The System.Data.Common factory that supports native and ODBC/OleDb providers
        /// </summary>
        /// <remarks>
        /// 
        /// API provides for varying degrees of convention and configuration.
        /// 
        /// </remarks>
        private readonly DbProviderFactory provider;

        public DbProviderFactory ProviderFactory
        {
            get { return provider; }
        }

        private DbBrand brandBackingVariable;

        /// <summary>
        /// A more specific way to ID the database than the provider string.
        /// </summary>
        private DbBrand brand
        {
            get
            {
                DbConfigUtils utils = new DbConfigUtils();
                if (utils.AppSettingIfExistsElseNull("Dataglot.Brand") != null)
                {
                    TraceApp.Verbose(() => "Found brand in config file, using :" + utils.AppSettingIfExistsElseNull("Dataglot.Brand"));
                    return (DbBrand)Enum.Parse(typeof(DbBrand), utils.AppSettingIfExistsElseNull("Dataglot.Brand"), true);
                }
                else
                {
                    return brandBackingVariable;
                }
            }
            set { brandBackingVariable = value; }
        }

        //private string oleDbConnectionString;

        //private string odbcConnectionString;

        private string nativeConnectionString;

        public NameValueCollection AppSettings = ConfigurationManager.AppSettings;

        public ConnectionStringSettingsCollection ConnectionStrings = ConfigurationManager.ConnectionStrings;

        //private string oleDbProvider;
        /// <summary>
        /// Check for appSettings, if found use them.
        /// Assume AspNetDb in an user instance on SQLExpress in the DataDirectory
        /// </summary>
        public DataFactory()
        {
            TraceApp.Verbose(() => "Default constructor, assuming SQLEXPRESS and the aspnetdb in a user instance");
            nativeConnectionString = @"data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnetdb.mdf;User Instance=true";

            provider = DbProviderFactories.GetFactory("System.Data.SqlClient");
            //TODO: Interogate DB for version
            brand = DbBrand.MsSql2000;
        }

        [Conditional("DEBUG")]
        public void ValidateInitialState()
        {
            //if (oleDbConnectionString == null)
            //{
            //    //Did we expect one?
            //    TraceApp.Verbose("No oledbConnection string. Hope we didn't need one.");
            //}

            //if (odbcConnectionString == null)
            //{
            //    //Did we expect one?
            //    TraceApp.Verbose("No ODBC string. Hope we didn't need one.");
            //}

            //if (nativeConnectionString == null)
            //{
            //    //Did we expect one?
            //    TraceApp.Verbose("No native string. Hope we didn't need one.");
            //}

            //if (string.IsNullOrEmpty(oleDbProvider) && !string.IsNullOrEmpty(oleDbConnectionString))
            //{
            //    throw new InvalidOperationException("If we have an oledb connection string, we need a provider name");
            //}

            //if (string.IsNullOrEmpty(oleDbConnectionString) &&
            //    string.IsNullOrEmpty(odbcConnectionString) &&
            //    string.IsNullOrEmpty(nativeConnectionString)
            //    )
            //{
            //    throw new InvalidOperationException("No connection string available, not oledb, odbc, nor native");
            //}
        }

        //Assumes SQL
        public DbConnection CreateConnection()
        {
            DbConnection con = provider.CreateConnection(nativeConnectionString);
            return con;
        }

        //Assumes SQL
        public DbConnection CreateOpenConnection()
        {
            DbConnection con = provider.CreateConnection(nativeConnectionString);
            con.Open();
            return con;
        }

        public FeatureSupportInfo SupportInfo { get; set; }

        private void ValidateConnectionString(string name)
        {
            if (ConfigurationManager.ConnectionStrings[name] == null)
            {
                throw new InvalidOperationException("Expected connection string name of " + name + " but didn't find it");
            }
        }

        ///// <summary>
        ///// Explicitly configure some oleDb 
        ///// </summary>
        //public DataFactory(string clientConnectionString, string providerName)
        //{
        //    TraceApp.Verbose(() => "Not-necessarily oledb, entire string provided and a provider : " + providerName);

        //    oleDbProvider = providerName;
        //    ConvertProvidertoBrand(providerName);

        //    if(!clientConnectionString.ToLower().Contains("Provider="))
        //    {
        //        //Assume native
        //        nativeConnectionString = clientConnectionString;
        //        if(brand == DbBrand.MsSql2000|| brand== DbBrand.MsSql2005)
        //        {
        //            oleDbConnectionString = ConvertNativeSqlToOleDb(nativeConnectionString);
        //        }
        //        else
        //        {
        //            throw new InvalidOperationException("Don't know how to convert a seemingly native " + provider + " connection string to Oledb");
        //        }
        //    }
        //    else
        //    {
        //        //Eg. MS-Access, or if user is using only Oledb interface
        //        oleDbConnectionString = clientConnectionString;
        //        nativeConnectionString = clientConnectionString;
        //    }

        //    //TODO: validate providerName
        //    provider = DbProviderFactories.GetFactory(providerName);

        //    TraceApp.Verbose(() => "I think we have a provider of : " + this.oleDbProvider);
        //    TraceApp.Verbose(() => "I think we have a brand of : " + CurrentDb);
        //}


        public DataFactory(FeatureSupportInfo supportInfo, string nativeConnString)
        {
            TraceApp.Verbose(() => "Creating an factory by brand for : " + supportInfo.Brand);

            if (ConnectionStrings[nativeConnString] != null)
            {
                //That's actually a name
                nativeConnectionString = ConnectionStrings[nativeConnString].ConnectionString;
                supportInfo.oleDbProvider = ConnectionStrings[nativeConnString].ProviderName;
                //provider = ConnectionStrings[nativeConnString].ProviderName;
            }
            else
            {
                nativeConnectionString = nativeConnString;
            }
            
        
            //TODO: Convert native to OleDB
            //if(targetBrand == DbBrand.MsSql2005 || targetBrand==DbBrand.MsSql2000)
            //    oleDbConnectionString = ConvertNativeSqlToOleDb(nativeConnString);
            //else
            //{
            //    oleDbConnectionString = nativeConnString;
            //}

            SupportInfo = supportInfo;
            provider = DbProviderFactories.GetFactory(ConvertBrandToProvider(SupportInfo.Brand));
        }





        private void ConvertProvidertoBrand(string providerName)
        {
            switch (providerName)
            {
                case "System.Data.OleDb":
                    // Could be many things
                    //brand = 
                    break;
                case "System.Data.Odbc":
                    // Could be many things
                    //brand = 
                    break;
                case "System.Data.SqlServerCe.3.5":
                    brand = DbBrand.SqlCompact35;
                    break;
                case "System.Data.SqlServerCe.4.0":
                    brand = DbBrand.SqlCompact4;
                    break;
                case "System.Data.OracleClient":
                    brand = DbBrand.MsOracle;
                    break;
                case "System.Data.SqlClient":
                    brand = DbBrand.MsSql2000;
                    //DbBrand.MsSql2005:
                    break;
                case "MySql.Data.MySqlClient":
                    brand = DbBrand.MySql;
                    break;
                case "System.Data.SQLite":
                    brand = DbBrand.SQLite;
                    break;
                default:
                    throw new InvalidOperationException("Can't use this API, unrecognized provider: " + providerName);
            }
        }

        private string ConvertBrandToProvider(DbBrand targetBrand)
        {
            //System.Data.Odbc
            //System.Data.OleDb
            //System.Data.OracleClient
            //System.Data.SqlClient
            //System.Data.SqlServerCe.3.5
            //System.Data.SqlServerCe.4.0

            switch (targetBrand)
            {
                case DbBrand.MsAccess:
                case DbBrand.OleDb:
                    return "System.Data.OleDb";
                case DbBrand.Odbc:
                    return "System.Data.OleDb";//BUG
                case DbBrand.SqlCompact4:
                    return "System.Data.SqlServerCe.4.0";
                case DbBrand.MsOracle:
                    return "System.Data.OracleClient";
                case DbBrand.MsSql2000:
                case DbBrand.MsSql2005:
                    return "System.Data.SqlClient";
                default:
                    throw new InvalidOperationException("Can't use this API, unrecognized provider : " + targetBrand);
            }
        }

        //private DbConfigUtils config = null;

        public DbBrand CurrentDb
        {
            get { return brand; }
        }

    }
}
